package com.wsoft.config;

import com.alibaba.fastjson.JSONObject;
import com.wsoft.constant.CommonConstant;
import com.wsoft.core.annotation.WLog;
import com.wsoft.core.utils.LoginUserUtil;
import com.wsoft.entity.SysLogEntity;
import com.wsoft.manager.SysLogManager;
import com.wsoft.vo.LoginUserVO;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import java.io.BufferedReader;
import java.io.IOException;
import java.lang.reflect.Method;
import java.time.Instant;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.time.ZoneOffset;
import java.util.Enumeration;
import java.util.concurrent.ThreadPoolExecutor;

/**
 * @Author z001
 * @Date 2024/11/19 13:35
 */
@Slf4j
@Aspect
@Component
public class OperateLogAspect {

    @Resource
    private SysLogManager sysLogManager;
    /**
     * 线程池
     */
    private ThreadPoolTaskExecutor taskExecutor;

    public OperateLogAspect() {
        this.initThread();
    }

    /**
     * 初始化线程池
     */
    private void initThread() {
        int corePoolSize = Runtime.getRuntime().availableProcessors();
        taskExecutor = new ThreadPoolTaskExecutor();
        //线程初始化
        taskExecutor.initialize();
        // 设置核心线程数
        taskExecutor.setCorePoolSize(corePoolSize);
        // 设置最大线程数
        taskExecutor.setMaxPoolSize(corePoolSize * 2);
        // 设置队列容量
        taskExecutor.setQueueCapacity(1000);
        // 设置线程活跃时间（秒）
        taskExecutor.setKeepAliveSeconds(60);
        // 设置默认线程名称
        taskExecutor.setThreadNamePrefix("smart-operate-log");
        // 设置拒绝策略
        taskExecutor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
        // 等待所有任务结束后再关闭线程池
        taskExecutor.setWaitForTasksToCompleteOnShutdown(true);
    }

    @Pointcut("@annotation(com.wsoft.core.annotation.WLog)")
    public void logPointCut() {
    }

    @Around("logPointCut()")
    public Object handleLog(ProceedingJoinPoint joinPoint) throws Throwable {
        SysLogEntity sysLog = new SysLogEntity();
        // 获取请求信息
        ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
        HttpServletRequest request = attributes.getRequest();
        // 获取注解信息
        MethodSignature signature = (MethodSignature) joinPoint.getSignature();
        Method method = signature.getMethod();
        WLog wLog = method.getAnnotation(WLog.class);

        Object[] args = joinPoint.getArgs();
        String params = getRequestParams(request);

        String className = joinPoint.getTarget().getClass().getName();
        String methodName = joinPoint.getSignature().getName();
        String operateMethod = className + "." + methodName;

        LoginUserVO loginUserVO = LoginUserUtil.getUserInfo().getLoginUserVO();
        sysLog.setUserId(loginUserVO.getId().toString());
        sysLog.setOrgId(loginUserVO.getLoginOrgId());

        sysLog.setTitle(wLog.title());
        sysLog.setRemark(wLog.desc());

        sysLog.setUrl(request.getRequestURI());
        sysLog.setIp(request.getRemoteAddr());
        sysLog.setMethod(operateMethod);
        sysLog.setParamsJson(params);
        sysLog.setCreateTime(getNowTime());
        sysLog.setType(CommonConstant.LOG_TYPE_BUSINESS);

        Object result = null;
        try {
            result = joinPoint.proceed();
            sysLog.setResultJson(JSONObject.toJSONString(result));
            sysLog.setIsException(0);
        } catch (Throwable e) {
            sysLog.setIsException(1);
            sysLog.setExceptionInfo(e.getMessage());
            throw e;
        } finally {
            sysLog.setRuntime( calculateIntervalInMilliseconds(sysLog.getCreateTime()));
            //异步保存日志
            taskExecutor.execute(() -> saveLog(sysLog));
        }

        return result;
    }


    /**
     * 保存日志
     *
     * @param operateLogEntity
     */
    private void saveLog(SysLogEntity operateLogEntity) {
        sysLogManager.save(operateLogEntity);
    }


    /**
     * 获取当前时间的毫秒值
     */
    public LocalDateTime getNowTime(){
        // 获取当前时间的毫秒值
        long currentTimeMillis = System.currentTimeMillis();
        // 将毫秒值转换为 Instant 对象
        Instant instant = Instant.ofEpochMilli(currentTimeMillis);
        // 将 Instant 对象转换为 LocalDateTime 对象，使用系统默认时区
        return LocalDateTime.ofInstant(instant, ZoneId.systemDefault());
    }

    /**
     * 计算 givenDateTime 和 当前时间的 时间差 单位毫秒
     * @param givenDateTime
     * @return
     */
    public  long calculateIntervalInMilliseconds(LocalDateTime givenDateTime) {
        // 获取当前的 LocalDateTime 对象
        LocalDateTime now = getNowTime();

        // 将给定的 LocalDateTime 对象转换为 Instant 对象
        long givenDateTimeMillis = givenDateTime.toInstant(ZoneOffset.UTC).toEpochMilli();

        // 将当前的 LocalDateTime 对象转换为 Instant 对象
        long nowMillis = now.toInstant(ZoneOffset.UTC).toEpochMilli();

        // 计算时间间隔（以毫秒为单位）
        return nowMillis - givenDateTimeMillis;
    }

    /**
     * 获取get/post 请求参数
     * @param request
     * @return
     * @throws IOException
     */
    private String getRequestParams(HttpServletRequest request) throws IOException {
        StringBuilder params = new StringBuilder();

        // 获取 GET 请求的参数
        if (CommonConstant.HTTP_GET.equalsIgnoreCase(request.getMethod())) {
            Enumeration<String> paramNames = request.getParameterNames();
            while (paramNames.hasMoreElements()) {
                String paramName = paramNames.nextElement();
                String paramValue = request.getParameter(paramName);
                params.append(paramName).append("=").append(paramValue).append("&");
            }
            if (params.length() > 0) {
                // 去掉最后一个 &
                params.deleteCharAt(params.length() - 1);
            }
        }

        // 获取 POST 请求的参数
        if (CommonConstant.HTTP_POST.equalsIgnoreCase(request.getMethod())) {
            BufferedReader reader = request.getReader();
            String line;
            while ((line = reader.readLine()) != null) {
                params.append(line);
            }
        }

        return params.toString();
    }


}
